﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;

namespace DelegateDemo
{

    //委托是引用类型
    //delegate与class是同一级别的
    //普通方法传递的是变量，而委托传递的是方法 
    //含有相同参数列以及返回值的方法的引用

    //【1】声明委托
    delegate void PlanToDo();


    public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();

            // 初始化绑定公共的委托方法
            CommonMethod.Method += SendInfo;
        }

        #region 认识委托



        //【2】创建委托实例
        PlanToDo planTo;


        Action<string> action;


        Func<int, int, int> func;

        private void GoShopping()
        {
            txt_Info.AppendText("买点零食" + "\r\n");
        }

        private void GoSwimming() => txt_Info.AppendText("去游泳" + "\r\n");

        private void ClimbMontain() => txt_Info.AppendText("去登山" + "\r\n");


        private void PlanToDoSth(PlanToDo planTo)
        {
            txt_Info.AppendText("6点起床" + "\r\n");
            txt_Info.AppendText("7点吃饭" + "\r\n");

            planTo();

            txt_Info.AppendText("22点睡觉" + "\r\n");
        }
        /// <summary>
        /// delegate委托
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_TestDelegate_Click(object sender, EventArgs e)
        {
            //可以不用显示地实例化
            //planTo = new PlanToDo(GoShopping);

            //【3】传入同样参数列表及返回值的方法
            planTo = GoSwimming;

            //【4】调用委托
            planTo?.Invoke();

            //if (planTo != null) { planTo(); }


            PlanToDoSth(GoSwimming);
        }


        //action委托的方法引用
        private void DoSth(string str)
        {
            txt_Info.AppendText(str + "\r\n");
        }


        //Func委托的方法引用
        private int AddMethod(int a, int b)
        {
            return a + b;
        }

        /// <summary>
        /// Action委托 无返回值，最多16个参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Action_Click(object sender, EventArgs e)
        {
            action = DoSth;
            action("玩一会儿超级玛丽");
        }

        /// <summary>
        /// Func委托 最后一个参数为返回值，最多16个参数 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Func_Click(object sender, EventArgs e)
        {
            func = AddMethod;
            int res = func(100, 200);

            txt_Info.AppendText(res + "\r\n");
        }
        /// <summary>
        /// 清空消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lbl_Clear_DoubleClick(object sender, EventArgs e)
        {
            this.txt_Info.Clear();
        }

        private void btn_NoName_Click(object sender, EventArgs e)
        {
            // 匿名函数
            // func=delegate(int a,int b) { return a + b; };

            // lambda表达式
            // func = (int a, int b)=> { return a + b; };

            // lambda表达式,参数类型可以省略(类型由委托推断)
            // func = (a, b) => { return a + b; };

            // lambda表达式,单行语句可以省略大括号和return
            func = (a, b) => a + b;
            int res = func(100, 200);
            txt_Info.AppendText(res + "\r\n");
        }




        #endregion

        //【2】创建委托的方法体
        private void SendInfo(string str)
        {
            txt_Info.AppendText(str + "\r\n");
        }

        private void btn_Form_Click(object sender, EventArgs e)
        {
            FrmOne frmOne = new FrmOne();


            //【3】委托绑定方法(委托返回值)
            frmOne.SendMsg += SendInfo;


            #region 使用窗体返回值进行传递
            DialogResult result = frmOne.ShowDialog();

            if (result == DialogResult.OK)
            {
                txt_Info.AppendText(frmOne.Info + "\r\n");
            }
            #endregion

        }


        Action asyncDelegate;

        Action<string, int> asyncDelegate2;

        Func<int, int, int> GetSumDelegate;

        public void T1()
        {
            this.Invoke(new Action(() =>
            {
                txt_Info.AppendText("T1\r\n");
            }));

            //Thread.Sleep(1000);不能写在this.Invoke里,主线程会卡住(this.Invoke切换到UI线程).
            Thread.Sleep(1000);

            this.Invoke(new Action(() =>
            {
                txt_Info.AppendText("T2\r\n");
            }));
        }

        public void T2(string s1, int n1)
        {
            this.Invoke(new Action(() =>
            {
                txt_Info.AppendText($"s1={s1}\r\n");
            }));

            //Thread.Sleep(1000);不能写在this.Invoke里,主线程会卡住(this.Invoke切换到UI线程).
            Thread.Sleep(1000);

            this.Invoke(new Action(() =>
            {
                txt_Info.AppendText($"n1={n1}\r\n");
            }));
        }

        public int T3(int f, int t)
        {
            int sum = 0;

            for (int i = f; i <= t; i++)
            {
                sum += i;
                Thread.Sleep(300);
            }

            return sum;
        }

        /// <summary>
        /// BeginInvoke 是旧的 APM(异步编程模型)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAsyncDelgate_Click(object sender, EventArgs e)
        {
            // 注：委托的异步调用，只支持单播委托

            #region 异步调用无参数无返回值的委托
            //// 同步委托调用是要等委托代理的方法执完毕后，才能执行后续代码,异步不需要等
            ////asyncDelegate = new Action(T1);
            //asyncDelegate = T1;

            //// 如果调用完异步委托后不需要回调，可以直接用BeginInvoke(null,null)
            //// IAsyncResult result = asyncDelegate.BeginInvoke(null, null);

            //// 如果需要回调，则传入回调方法(可以将需要委托执行完才执行的逻辑交给回调方法)
            //IAsyncResult result = asyncDelegate.BeginInvoke(OnAsyncComplete, null);

            //// 不等待,立即继续执行
            //txt_Info.AppendText("End\r\n");
            #endregion

            #region 异步调用带参数无返回值的委托
            ////asyncDelegate2 = new Action<string,int>(T2);
            //asyncDelegate2 = T2;
            //// 开始异步调用委托,传入参数(不需要回调)
            //////asyncDelegate2.BeginInvoke("zhangsan", 18, null, null);

            //// 开始异步调用委托,传入参数(需要回调)
            //asyncDelegate2.BeginInvoke("zhangsan", 18, OnAsyncComplete2, null);

            //txt_Info.AppendText("End\r\n");
            #endregion

            #region 异步调用带参数,带返回值的委托
            ////GetSumDelegate = new Func<int, int, int>(T3);
            GetSumDelegate = T3;

            // 开始异步调用委托
            GetSumDelegate.BeginInvoke(1, 10, new AsyncCallback(ar =>
            {
                // 回调函数中首先把参数IAsyncResult转换为AsyncResult类型,方便后面的使用(AsyncResult调用的成员更多)
                AsyncResult result = ar as AsyncResult;
                // 获取用户在异步调用委托时传递过来的最后一个object参数
                this.Invoke(new Action(() => txt_Info.AppendText("调用委托传入的object类型参数:" + result.AsyncState.ToString() + "\r\n")));
                // 获取当前被异步调用的委托实例
                Func<int, int, int> currentDelegate = result.AsyncDelegate as Func<int, int, int>;
                // 获取委托异步调用后的返回值
                int sum = currentDelegate.EndInvoke(ar);
                // 在UI线程中更新UI
                this.Invoke(new Action(() => txt_Info.AppendText($"最后的结果是:{sum}\r\n")));
                // 这里是回调方法,在异步线程池线程里执行   
            }), "Param1"); // 这里的"Param1"会传给IAsyncResult.AsyncState属性(是Object类型)

            txt_Info.AppendText("异步委托已经开始调用了!!!!!!\r\n");
            txt_Info.AppendText("End\r\n");
            #endregion


        }

        private void OnAsyncComplete(IAsyncResult result)
        {
            // 回调在线程池线程执行
            // 线程阻塞，等待异步委托调用结束.异步委托使用线程池里的线程(后台线程),不会阻塞UI线程
            asyncDelegate.EndInvoke(result);

            // 如果需要通知 UI，记得用 Invoke
            this.Invoke(new Action(() =>
            {
                txt_Info.AppendText("异步调用完成\r\n");
            }));
        }

        private void OnAsyncComplete2(IAsyncResult result)
        {
            // 回调在线程池线程执行
            // 线程阻塞，等待异步委托调用结束.异步委托使用线程池里的线程(后台线程),不会阻塞UI线程
            asyncDelegate2.EndInvoke(result);

            // 如果需要通知 UI，记得用 Invoke
            this.Invoke(new Action(() =>
            {
                txt_Info.AppendText("异步调用完成\r\n");
            }));
        }


        /// <summary>
        /// 使用现代异步编程模型（推荐替代 BeginInvoke/ EndInvoke）
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //private async void btnAsyncNow_Click(object sender, EventArgs e)
        //{
        //    

        //    //// T1在后台线程运行,这里加了await,所以会等T1执行完毕后,才执行下面的代码
        //    //await Task.Run(() => T1());

        //    //txt_Info.AppendText("End\r\n");
        //}

        /// <summary>
        /// 使用现代异步编程模型（推荐替代 BeginInvoke/ EndInvoke）
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAsyncNow_Click(object sender, EventArgs e)
        {


            //// T1在后台线程运行,这里加了await,所以会等T1执行完毕后,才执行下面的代码
            //await Task.Run(() => T1());

            //txt_Info.AppendText("End\r\n");

            // 如果不需要等T1执行完毕,就继续执行下面的代码,可以不加await
            _ = Task.Run(() => T1());
            txt_Info.AppendText("End\r\n");
        }
    }
}
